package com.spring.theory.mvc.basic.handlerMethod;

import com.spring.theory.mvc.basic.handlerMethod.HandlerWebConfig.HandlerController;
import com.spring.theory.mvc.basic.handlerMethod.HandlerWebConfig.HandlerUser;

import java.util.Collections;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver;
import org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver;
import org.springframework.web.method.annotation.RequestParamMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import org.springframework.web.servlet.mvc.method.annotation.ServletCookieValueMethodArgumentResolver;
import org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod;
import org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor;
import org.springframework.web.servlet.mvc.method.annotation.ServletRequestDataBinderFactory;
import org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver;

/**
 * TestHandlerMethod
 *
 * @author H
 * @version 1.0
 * @date 2024-05-09 16:02
 */
@Slf4j
public class TestHandlerMethod {

    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(HandlerWebConfig.class);

        MockHttpServletRequest request = new MockHttpServletRequest();
        request.setParameter("name", "mockHandlerRequest");

        /**
         * 准备bean和method
         */
        ServletInvocableHandlerMethod handlerMethod = new ServletInvocableHandlerMethod(new HandlerController(), HandlerController.class.getMethod("test", HandlerUser.class));
        /**
         * 1. 控制器方法添加参数解析器
         */
        handlerMethod.setHandlerMethodArgumentResolvers(getArgumentResolvers(context.getDefaultListableBeanFactory()));
        /**
         * 2. 控制器方法添加数据绑定器；对象绑定与类型转换
         */
        handlerMethod.setDataBinderFactory(new ServletRequestDataBinderFactory(null, null));
        /**
         * 3. 控制器方法添加参数名称解析器；spring提供的默认参数名称解析器
         */
        handlerMethod.setParameterNameDiscoverer(new DefaultParameterNameDiscoverer());
        /**
         * 4. 添加控制器方法模型与视图容器
         */
        ModelAndViewContainer container = new ModelAndViewContainer();
        //执行控制器方法
        handlerMethod.invokeAndHandle(new ServletWebRequest(request), container);
        log.info("container:{}", container.getModel());

        context.close();

    }

    static HandlerMethodArgumentResolverComposite getArgumentResolvers(ConfigurableBeanFactory beanFactory) {
        HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite();
        composite.addResolvers(
                new RequestParamMethodArgumentResolver(beanFactory, false),
                new PathVariableMethodArgumentResolver(),
                new RequestHeaderMethodArgumentResolver(beanFactory),
                new ServletCookieValueMethodArgumentResolver(beanFactory),
                new ExpressionValueMethodArgumentResolver(beanFactory),
                new ServletRequestMethodArgumentResolver(),
                new ServletModelAttributeMethodProcessor(false),
                new RequestResponseBodyMethodProcessor(Collections.singletonList(new MappingJackson2HttpMessageConverter())),
                new ServletModelAttributeMethodProcessor(true),
                new RequestParamMethodArgumentResolver(beanFactory, true)
        );
        return composite;
    }

}
